/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package wekaGrexBridge;

import grex.ErrorManagement.ErrorManager;
import grex.EvolutionProcess;
import grex.GP;
import grex.Options;
import grex.Data.Prediction;
import grex.Data.PredictionContainer;
import grex.Graphics.ExternalEvolutionFrame;
import grex.Nodes.GeneException;

import java.io.File;
import java.io.FileFilter;
import java.io.Serializable;
import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;

import weka.core.Instance;
import weka.core.Instances;

public class WekaWrapper implements Serializable {

	private static final long serialVersionUID = -7008999491875273936L;
	GP model;
    Instances instances;
    Options options;
    WekaArffTableModel data;
    String[] classValues;
    boolean classifcationTask;
    int nrOfTargets;
    
    public WekaWrapper(Instances instances, Options options) throws Exception {
        this.instances = instances;
        options.setTRAIN_SIZE(100);
        options.setTEST_SIZE(0);
        this.options = options;
        int classes = instances.classAttribute().numValues();
        int attributes = instances.numAttributes();
        if (instances.attribute(attributes - 2).name().startsWith("probability_")) {
            classValues = new String[classes];
            for (int classIndex = 0; classIndex < classes; classIndex++) {
                classValues[classIndex] = instances.classAttribute().value(classIndex);
            }
            data = new WekaArffTableModel(new J48Injection(), instances, classValues, this.options);
        } else {
            data = new WekaArffTableModel(new J48Injection(), instances);
        }
        System.out.println("New Model Initilized");
        nrOfTargets = data.getNrOfTargetCategories();
        classifcationTask = data.isClassificationTask();
    }

    //Call this method to create a new model
    public void buildWraper() {
        try {
            System.out.println("Bulding model");
            EvolutionProcess ep = new EvolutionProcess(data, options);

            new ExternalEvolutionFrame(ep, options);

            System.out.println(options);
            ep.startEvolution(options);
            System.out.println("waiting for join");
          //  Thread.sleep(10000);
            ep.getThread().join();
            System.out.println("Joined");
            model = ep.getBestGP();
            System.out.println("Evolution finished");
            /*try {
                PredictionContainer test = new PredictionContainer(100);
                for(Prediction p:model.getPcTrain().values()){
                    p.resetPredictionCalculations();
                    test.put(p.getInstance(), new Prediction(p.getInstance()));
                }
                double tacc = 0;// model.getTrainAcc();
                model.execute(model.getPcTrain());
                model.execute(test);
                double exAcc =0;

                for(Prediction p:test.values()){
                    double[] probs = p.getProbs();
                    double maxP = probs[0];
                    int maxI = 0;
                    for(int i = 1; i < probs.length; i++){
                        if(probs[i]>maxP){
                            maxP = probs[i];
                            maxI = i;
                        }
                    }
                    if(maxI == p.getTargetValue())
                        tacc++;
                }
                for(Prediction p:model.getPcTrain().values()){
                    if(p.getPrediction() == p.getTargetValue())
                        exAcc++;
                }
                System.out.println("TestTrainACC:" + tacc/test.values().size() + " ExTrainAcc" + exAcc/model.getPcTrain().values().size() + " Instances:" + model.getPcTrain().values().size());
             */
             /* try {
                model.optimize();
                } catch (GeneException ex) {
                ex.printStackTrace();
                ErrorManager.getInstance().reportError(ex);
                }*/
            /*} catch (GeneException ex) {
                Logger.getLogger(WekaWrapper.class.getName()).log(Level.SEVERE, null, ex);
            }*/

        } catch (InterruptedException ex) {
            ex.printStackTrace();
            ErrorManager.getInstance().reportError(ex);
        }
    }

    public static String[] getDiredctoryBNFs() {
        //String directoryName;
        String[] bnfs;
        File directory;
        File[] files;

        directory = new File("." + File.separatorChar + "BNFs");
        FileFilter filter = new FileFilter() {

            @SuppressWarnings("unused")
			public String getDescription() {
                return "BNFs";
            }

            public boolean accept(File f) {
                if (f.isDirectory()) {
                    return false;
                }
                if (f.getName().endsWith(".txt")) {
                    return true;
                }
                if (f.getName().endsWith(".png")) {
                    return true;
                }
                return false;
            }
        };
        files = directory.listFiles(filter);
        bnfs = new String[files.length];

        for (int i = 0; i < bnfs.length; i++) {
            bnfs[i] = files[i].getName();

        }
        Arrays.sort(bnfs);
        return bnfs;
    }

    public static String[] getFitnessFunctions() {
        String[] cats = {
            "ProbFitness",
            "SingleProbFitness",
            "ACCFitness",
            "AUCFitness",
            "BRIFitness",
            "FuzzyFitness",
            "CombinedFitness",
            "BrevityFitness", 
            "RegressionFitness", 
            "GMRAEFitness", 
            "MUAPEFitness",
            "MultipleSolutionsFitness"
        };
        //String[] regs = {};
        //String[] functions;
        //if(isClassificationTask)
        return cats;
        // else
        //   return regs;
    }

    public double classifyInstance(Instance instance) throws Exception {
        PredictionContainer pc = new PredictionContainer(1);
        double[] row = data.getDoubleArray(instance);
        Prediction p = new Prediction(row,classifcationTask,nrOfTargets);
        pc.put(row, p);
        model.execute(pc);
        return p.getPrediction();
    }

    public double[] distributionForInstance(Instance instance) throws Exception {
        PredictionContainer pc = new PredictionContainer(1);
        double[] row = data.getDoubleArray(instance);
        Prediction p = new Prediction(row,classifcationTask,nrOfTargets);
        pc.put(row, p);
        model.execute(pc);
        return p.getProbs();
        /*double[] probs=p.getProbs();
        double[] oProbs= new double[probs.length];
        int[] probOrder=data.getProbOrder();
        for(int i=0;i<probs.length;i++)
        oProbs[probOrder[i]]=probs[i];
        return oProbs;*/
    }

    public String toString() {
        String result;
        try {
            result = model.getFormatedTree();
        } catch (Exception e) {
            result = model.toString();
        }
        return result;
    }

    public double getNumLeaves() {
        return model.getNrOfLeaves();
    }

    public double getTreeSize() {
            return model.getNrOfIfsAndLeafs();
    }
}
